home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_87 / songunit.pas < prev    next >
Pascal/Delphi Source File  |  1995-01-01  |  16KB  |  568 lines

  1. {****************************************************************************}
  2. {                                                                            }
  3. { MODULE:        SongUnit                                                    }
  4. {                                                                            }
  5. { DESCRIPTION:   Gives the necessary support for handling the different      }
  6. {                data types and different file formats of a song. Also, it   }
  7. {                implements the base routines for loading the song from many }
  8. {                different file formats and (future) saving them to disk.    }
  9. {                                                                            }
  10. { AUTHOR:        Juan Carlos Arévalo Baeza                                   }
  11. {                                                                            }
  12. { MODIFICATIONS: Nobody (yet).                                               }
  13. {                                                                            }
  14. { HISTORY:       xx-May-1992 First implementations (lost in the memory of    }
  15. {                            time O:-).                                      }
  16. {                xx-Jun-1992 Lots of improvements (ditto O;-).               }
  17. {                11-Jul-1992 Started first documented version.               }
  18. {                21-Oct-1992 Rechecking. First remodeling.                   }
  19. {                25-Jan-1993 Created the .OKT and .WOW loader.               }
  20. {                06-Feb-1993 Remodelling. Made the memory-optimized, object- }
  21. {                            oriented interface. Name change from ModUnit.   }
  22. {                                                                            }
  23. { (C) 1992, 1993 VangeliSTeam                                                }
  24. {____________________________________________________________________________}
  25.  
  26. UNIT SongUnit;
  27.  
  28. INTERFACE
  29.  
  30. USES Dos, Objects,
  31.      HexConversions,
  32.      SongElements;
  33.  
  34.  
  35.  
  36.  
  37. {----------------------------------------------------------------------------}
  38. { Song object definition.                                                    }
  39. {____________________________________________________________________________}
  40.  
  41. TYPE
  42.   TSongFileFormat =
  43.     (
  44.       mffUnknown      ,    { Unknown format O:-)                      }
  45.       mffMod31M_K_    ,    { Protracker "M.K.".                       }
  46.       mffMod31FLT4    ,    { Protracker "FLT4".                       }
  47.       mffMod15        ,    { SoundTracker 15-instrument module.       }
  48.       mffJMPlayer     ,    { JMPlayer module.                         }
  49.       mffOktalizer    ,    { 8 voices Oktalizer MOD.           (.OKT) }
  50.       mffComposer669  ,    { 8 voices Composer-669.            (.669) }
  51.       mffWow8         ,    { 8 voices Grave.                   (.WOW) }
  52.       mffFastTracker  ,    { 6 or 8 voices Triton FastTracker. (.MOD) }
  53.       mffS3m          ,    { ScreamTracker 3.0                 (.S3M) }
  54.       mffS2m          ,    { ScreamTracker 3.0 (beta)          (.S2M) }
  55.       mffStm               { ScreamTracker 2.x                 (.STM) }
  56.     );
  57.  
  58.   TSongStatus =
  59.     (
  60.       { Non fatal states }
  61.  
  62.       msNotLoaded              ,    { Not yet loaded                                         }
  63.       msOK                     ,    { Everything was Ok.                                     }
  64.       msFileTooShort           ,    { End of file premature (lot's of modules have this).    }
  65.  
  66.       { Fatal states }
  67.  
  68.       msFileOpenError          ,    { Could not open the .MOD file.                          }
  69.       msOutOfMemory            ,    { There is not enough memory left. :-( Shouldn't happen. }
  70.       msFileDamaged            ,    { Syntax checking error on module file.                  }
  71.       msFileFormatNotSupported      { JMPlayer or ScreamTracker, for example.                }
  72.     );
  73.  
  74.  
  75. TYPE
  76.   PSong = ^TSong;
  77.   TSong =
  78.     OBJECT(TObject)
  79.  
  80.       { Desired data }
  81.  
  82.       SongStart             : WORD;
  83.       SongLen               : WORD;
  84.  
  85.       { General song data }
  86.  
  87.       Name                  : PString;
  88.       InsidePath            : PString;
  89.       Comment               : PSongComment;
  90.       FileDir               : PString;
  91.       FileName              : NameStr;
  92.       FileExt               : ExtStr;
  93.       FirstTick             : BOOLEAN;
  94.       InitialTempo          : BYTE;
  95.       InitialBPM            : BYTE;
  96.       Volume                : BYTE;
  97.       NumChannels           : BYTE;
  98.  
  99.       { Instrument data }
  100.  
  101.       Instruments           : TCollection;
  102.  
  103.       { Pattern sequence data }
  104.  
  105.       SequenceLength        : WORD;
  106.       SequenceRepStart      : WORD;
  107.       PatternSequence       : PPatternSequence;
  108.       PatternTempos         : PPatternSequence;
  109.  
  110.       Patterns              : TCollection;
  111.  
  112.       { Track data }
  113.  
  114.       Tracks                : TCollection;
  115.  
  116.       { State data }
  117.  
  118.       Status                : TSongStatus;
  119.       ErrorCode             : WORD;
  120.       ThereIsMore           : BOOLEAN;
  121.       FileFormat            : TSongFileFormat;
  122.  
  123.  
  124.  
  125.       { Methods }
  126.  
  127.       CONSTRUCTOR Init;
  128.       DESTRUCTOR  Done; VIRTUAL;
  129.  
  130.       PROCEDURE Load(VAR St: TStream);
  131.       PROCEDURE Save(VAR St: TStream);
  132.  
  133.       PROCEDURE LoadFName(FName: PathStr);
  134.       PROCEDURE SaveFName(FName: PathStr);
  135.  
  136.       PROCEDURE Free;
  137.       PROCEDURE InitValues;
  138.       PROCEDURE Empty;
  139.  
  140.       FUNCTION  GetErrorString                            : STRING;
  141.  
  142.       FUNCTION  GetName                                   : STRING;
  143.       FUNCTION  GetInsidePath                             : STRING;
  144.       FUNCTION  GetInstrument      (i: WORD)              : PInstrument;
  145.       FUNCTION  GetTrack           (i: WORD)              : PTrack;
  146.       FUNCTION  GetPattern         (i: WORD)              : PPattern;
  147.       FUNCTION  GetPatternSeq      (i: WORD)              : PPattern;
  148.       FUNCTION  GetPatternSequence (Seq: WORD)            : WORD;
  149.       FUNCTION  GetPatternTempo    (Seq: WORD)            : WORD;
  150.       PROCEDURE GetNote            (Seq, Row, Chan: WORD; VAR Note: TFullNote);
  151.  
  152.       PROCEDURE SetName            (S: STRING);
  153.       PROCEDURE SetInsidePath      (S: STRING);
  154.     END;
  155.  
  156.  
  157.  
  158.  
  159. {----------------------------------------------------------------------------}
  160. { Header definition for the loaders.                                         }
  161. {____________________________________________________________________________}
  162.  
  163. TYPE
  164.   PSongHeader = ^TSongHeader;
  165.   TSongHeader = ARRAY[0..2047] OF BYTE;
  166.  
  167.  
  168.  
  169.  
  170. IMPLEMENTATION
  171.  
  172. USES SongUtils,
  173.      UnkLoader, ModLoader, OktLoader, S3mLoader, StmLoader, Loader669, ExeLoader,
  174.      Heaps,
  175.      StrConst, AsciiZ, Filters;
  176.  
  177.  
  178.  
  179.  
  180. {----------------------------------------------------------------------------}
  181. { Loaders definition.                                                        }
  182. {____________________________________________________________________________}
  183.  
  184. TYPE
  185.   TSongLoader = PROCEDURE (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  186.  
  187. CONST
  188.   NumLoaders = 8;
  189.  
  190.   SongLoaders : ARRAY[1..NumLoaders] OF TSongLoader =
  191.     (
  192.       LoadJMFileFormat,
  193.       Load669FileFormat,
  194.       LoadOktFileFormat,
  195.       LoadS2mFileFormat,
  196.       LoadS3mFileFormat,
  197.       LoadStmFileFormat,
  198.       LoadExeFileFormat,
  199.       LoadModFileFormat
  200.     );
  201.  
  202.  
  203.  
  204.  
  205. {----------------------------------------------------------------------------}
  206. { TSong object.                                                              }
  207. {____________________________________________________________________________}
  208.  
  209. CONSTRUCTOR TSong.Init;
  210.   BEGIN
  211.     TObject.Init;
  212.     InitValues;
  213.   END;
  214.  
  215.  
  216. DESTRUCTOR  TSong.Done; 
  217.   BEGIN
  218.     Free;
  219.     TObject.Done;
  220.   END;
  221.  
  222.  
  223. PROCEDURE TSong.Load(VAR St: TStream);
  224.   VAR
  225.     Header : TSongHeader;
  226.     i      : WORD;
  227.     Pos    : LONGINT;
  228.   BEGIN
  229.     Pos := St.GetPos;
  230.  
  231.     ThereIsMore := FALSE;
  232.  
  233.     St.Read(Header, SIZEOF(TSongHeader));
  234.  
  235.     IF St.Status <> stOk THEN
  236.       BEGIN
  237.         Status    := msFileDamaged;
  238.         ErrorCode := St.ErrorInfo;
  239.         St.Done;
  240.         EXIT;
  241.       END;
  242.  
  243.     i := 1;
  244.     WHILE (i     <= NumLoaders)  AND
  245.           (Status = msNotLoaded) DO
  246.       BEGIN
  247.         St.Seek(Pos);
  248.         SongLoaders[i](PSong(@Self)^, St, Header);
  249.         INC(i);
  250.       END;
  251.   END;
  252.  
  253.  
  254. PROCEDURE TSong.LoadFName(FName: PathStr);
  255.   VAR
  256.     St         : TDosStream;
  257.     Dir        : DirStr;
  258.     IPath      : STRING[12];
  259.     OSongStart : WORD;
  260.     OSongLen   : WORD;
  261.   BEGIN
  262.     OSongStart := SongStart;
  263.     OSongLen   := SongLen;
  264.     IPath := GetInsidePath;
  265.     Empty;
  266.     SetInsidePath(IPath);
  267.     SongStart := OSongStart;
  268.     SongLen   := OSongLen;
  269.  
  270.     FName := FExpand(FName);
  271.     FSplit(FName, Dir, FileName, FileExt);
  272.     FileDir := FullHeap.HNewStr(Dir);
  273.     IF FileExt = '' THEN FileExt := '.MOD';
  274.     FName := Dir+FileName+FileExt;
  275.  
  276.     St.Init(FName, stOpenRead);
  277.  
  278.     IF St.Status <> stOk THEN
  279.       BEGIN
  280.         Status    := msFileOpenError;
  281.         ErrorCode := St.ErrorInfo;
  282.         St.Done;
  283.         EXIT;
  284.       END;
  285.  
  286.     Status    := msNotLoaded;
  287.     ErrorCode := 0;
  288.  
  289.     Load(St);
  290.  
  291.     IF Status <> msOk THEN
  292.       ErrorCode := St.ErrorInfo;
  293.  
  294.     St.Done;
  295.   END;
  296.  
  297.  
  298. PROCEDURE TSong.Save(VAR St: TStream);
  299.   BEGIN
  300.   END;
  301.  
  302.  
  303. PROCEDURE TSong.SaveFName(FName: PathStr);
  304.   BEGIN
  305.   END;
  306.  
  307.  
  308. FUNCTION TSong.GetErrorString : STRING;
  309.   BEGIN
  310.     CASE Status OF
  311.       msFileOpenError:          GetErrorString := GetString(StrFileOpenError);
  312.       msOutOfMemory:            GetErrorString := GetString(StrOutOfMemory);
  313.       msFileDamaged:            GetErrorString := GetString(StrFileDamaged);
  314.       msFileTooShort:           GetErrorString := GetString(StrFileTooShort);
  315.       msFileFormatNotSupported: GetErrorString := GetString(StrFileFormatNotSupported) +
  316.                                                   GetString(StrFileFormats + BYTE(FileFormat));
  317.       ELSE                      GetErrorString := '';
  318.     END;
  319.   END;
  320.  
  321.  
  322. FUNCTION TSong.GetName : STRING;
  323.   BEGIN
  324.     IF Name <> NIL THEN
  325.       GetName := Name^
  326.     ELSE
  327.       GetName := '';
  328.   END;
  329.  
  330.  
  331. PROCEDURE TSong.SetName(S: STRING);
  332.   BEGIN
  333.     IF Name <> NIL THEN
  334.       FullHeap.HDisposeStr(Name);
  335.  
  336.     IF S <> '' THEN
  337.       Name := FullHeap.HNewStr(S);
  338.   END;
  339.  
  340.  
  341. FUNCTION TSong.GetInsidePath : STRING;
  342.   BEGIN
  343.     IF InsidePath <> NIL THEN
  344.       GetInsidePath := InsidePath^
  345.     ELSE
  346.       GetInsidePath := '';
  347.   END;
  348.  
  349.  
  350. PROCEDURE TSong.SetInsidePath(S: STRING);
  351.   BEGIN
  352.     IF InsidePath <> NIL THEN
  353.       FullHeap.HDisposeStr(InsidePath);
  354.  
  355.     IF S <> '' THEN
  356.       InsidePath := FullHeap.HNewStr(S);
  357.   END;
  358.  
  359.  
  360. FUNCTION TSong.GetInstrument(i: WORD) : PInstrument;
  361.   VAR
  362.     Instrument : PInstrument;
  363.     j          : WORD;
  364.   LABEL
  365.     Break;
  366.   BEGIN
  367.     IF i >= Instruments.Count THEN
  368.       BEGIN
  369.         FOR j := Instruments.Count TO i DO
  370.           BEGIN
  371.             Heap.HGetMem(POINTER(Instrument), SizeOf(TInstrument));
  372.             IF Instrument <> NIL THEN
  373.               BEGIN
  374.                 Instrument^.Init;
  375.                 Instruments.AtInsert(j, Instrument);
  376.               END
  377.             ELSE
  378.               GOTO Break;
  379.           END;
  380. Break:
  381.         GetInstrument := Instrument;
  382.       END
  383.     ELSE
  384.       GetInstrument := PInstrument(Instruments.At(i));
  385.   END;
  386.  
  387.  
  388. FUNCTION TSong.GetTrack(i: WORD) : PTrack;
  389.   VAR
  390.     Track : PTrack;
  391.     j     : WORD;
  392.   LABEL
  393.     Break;
  394.   BEGIN
  395.     IF i >= Tracks.Count THEN
  396.       BEGIN
  397.         FOR j := Tracks.Count TO i DO
  398.           BEGIN
  399.             Heap.HGetMem(POINTER(Track), SizeOf(TTrack));
  400.             IF Track <> NIL THEN
  401.               BEGIN
  402.                 Track^.Init;
  403.                 Tracks.AtInsert(j, Track);
  404.               END
  405.             ELSE
  406.               GOTO Break;
  407.           END;
  408. Break:
  409.         GetTrack := Track;
  410.       END
  411.     ELSE
  412.       GetTrack := PTrack(Tracks.At(i));
  413.   END;
  414.  
  415.  
  416. FUNCTION TSong.GetPattern(i: WORD) : PPattern;
  417.   VAR
  418.     Pattern : PPattern;
  419.     j       : WORD;
  420.   LABEL
  421.     Break;
  422.   BEGIN
  423.     IF i >= Patterns.Count THEN
  424.       BEGIN
  425.         FOR j := Patterns.Count TO i DO
  426.           BEGIN
  427.             Heap.HGetMem(POINTER(Pattern), SizeOf(TPattern));
  428.             IF Pattern <> NIL THEN
  429.               BEGIN
  430.                 Pattern^.Init(NumChannels);
  431.                 Patterns.AtInsert(j, Pattern);
  432.               END
  433.             ELSE
  434.               GOTO Break;
  435.           END;
  436. Break:
  437.         GetPattern := Pattern;
  438.       END
  439.     ELSE
  440.       GetPattern := PPattern(Patterns.At(i));
  441.   END;
  442.  
  443.  
  444. FUNCTION TSong.GetPatternSeq(i: WORD) : PPattern;
  445.   BEGIN
  446.     GetPatternSeq := GetPattern(GetPatternSequence(i));
  447.   END;
  448.  
  449.  
  450. FUNCTION TSong.GetPatternSequence(Seq: WORD) : WORD;
  451.   BEGIN
  452.     IF PatternSequence <> NIL THEN
  453.       GetPatternSequence := PatternSequence^[WORD(Seq)]
  454.     ELSE
  455.       GetPatternSequence := 0;
  456.   END;
  457.  
  458.  
  459. FUNCTION TSong.GetPatternTempo(Seq: WORD) : WORD;
  460.   BEGIN
  461.     IF PatternTempos <> NIL THEN
  462.       GetPatternTempo := PatternTempos^[WORD(Seq)]
  463.     ELSE
  464.       GetPatternTempo := 0;
  465.   END;
  466.  
  467.  
  468. PROCEDURE TSong.GetNote(Seq, Row, Chan: WORD; VAR Note: TFullNote);
  469.   VAR
  470.     Patt  : PPattern;
  471.     Track : PTrack;
  472.     n     : WORD;
  473.     NOffs : WORD;
  474.   BEGIN
  475.     IF PatternSequence <> NIL THEN
  476.       BEGIN
  477.         Patt := GetPatternSeq(Seq);
  478.         IF Patt <> NIL THEN
  479.           BEGIN
  480.             n     := Patt^.Patt^.Channels[Chan];
  481.             Track := GetTrack(n);
  482.             IF Track <> NIL THEN
  483.               BEGIN
  484.                 Track^.GetNote(Row, Note);
  485.                 EXIT;
  486.               END
  487.           END
  488.       END;
  489.  
  490.     FillChar(Note, SizeOf(Note), 0);
  491.   END;
  492.  
  493.  
  494. PROCEDURE TSong.Free;
  495.   VAR
  496.     i : WORD;
  497.   BEGIN
  498.     ASM CLI END;
  499.  
  500.     FullHeap.HDisposeStr(Name);
  501.     FullHeap.HFreeMem   (POINTER(Comment), SizeOf(Comment^));
  502.     FullHeap.HDisposeStr(FileDir);
  503.  
  504.     Instruments.Done;
  505.  
  506.     FullHeap.HFreeMem(POINTER(PatternSequence), SizeOf(PatternSequence^));
  507.     FullHeap.HFreeMem(POINTER(PatternTempos),   SizeOf(PatternTempos^));
  508.     Patterns.Done;
  509.  
  510.     Tracks.Done;
  511.  
  512.     ASM STI END;
  513.   END;
  514.  
  515.  
  516.  
  517. PROCEDURE TSong.InitValues;
  518.   BEGIN
  519.     SongStart    := 1;
  520.     SongLen      := MaxSequence;
  521.  
  522.     Name         := NIL;
  523.     InsidePath   := NIL;
  524.     Comment      := NIL;
  525.     FileDir      := NIL;
  526.     FileName     := '';
  527.     FileExt      := '';
  528.     FirstTick    := FALSE;
  529.     InitialTempo := 1;
  530.     InitialBPM   := 1;
  531.     Volume       := 0;
  532.     NumChannels  := 0;
  533.  
  534.     Instruments.Init(32, 32);
  535.  
  536.     SequenceLength   := 0;
  537.     SequenceRepStart := 0;
  538.  
  539.     FullHeap.HGetMem(POINTER(PatternSequence), SizeOf(PatternSequence^));
  540.     FullHeap.HGetMem(POINTER(PatternTempos),   SizeOf(PatternTempos^));
  541.     IF PatternSequence <> NIL THEN
  542.       FillChar(PatternSequence^, SizeOf(PatternSequence^), 0);
  543.     IF PatternTempos <> NIL THEN
  544.       FillChar(PatternTempos^, SizeOf(PatternTempos^), 0);
  545.     Patterns.Init(64, 64);
  546.  
  547.     Tracks.Init(256, 256);
  548.  
  549.     Status      := msNotLoaded;
  550.     ErrorCode   := 0;
  551.     ThereIsMore := FALSE;
  552.     FileFormat  := mffUnknown;
  553.   END;
  554.  
  555.  
  556.  
  557.  
  558. PROCEDURE TSong.Empty;
  559.   BEGIN
  560.     Free;
  561.     InitValues;
  562.   END;
  563.  
  564.  
  565.  
  566.  
  567. END.
  568.